package cn.codeforfun.main;

import cn.codeforfun.utils.JsonUtils;
import cn.codeforfun.utils.RegexUtils;
import cn.codeforfun.utils.StringUtils;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author wangbin
 */
@Service
public class MainService {

    @Resource(name = "itemMap")
    private Map<String, String> itemMap;
    @Resource(name = "itemJs")
    private String itemJs;

    @Resource(name = "calcJs")
    private String calcJs;

    @Resource
    private RedisTemplate<String, String> redis;

    @Value("${cache.timeout}")
    private Integer cacheTimeout;

    OkHttpClient client = new OkHttpClient();

    public ModelAndView resolveSkill(String code) throws IOException {
        String result = getCache(code);
        Map<String, Object> map = JsonUtils.toMap(result);
        Map<String, Object> data = (Map<String, Object>) map.get("data");
        resolveSkill(data);
        return new ModelAndView("skill", map);
    }

    public ModelAndView resolveSimple(String code) throws IOException {
        String result = getCache(code);
        Map<String, Object> map = JsonUtils.toMap(result);
        Map<String, Object> data = (Map<String, Object>) map.get("data");
        resolveEquipment(data);
        return new ModelAndView("simple", map);
    }

    public ModelAndView resolveAll(String code) throws IOException {
        String result = getCache(code);
        Map<String, Object> map = JsonUtils.toMap(result);
        Map<String, Object> data = (Map<String, Object>) map.get("data");
        resolveEquipment(data);
        resolveSkill(data);
        return new ModelAndView("all", map);
    }

    private String getCache(String code) throws IOException {
        ValueOperations<String, String> ops = redis.opsForValue();
        String result = ops.get(code);
        if (result == null) {
            //第一步：生成随机数
            String cfdUid = StringUtils.getRandomStringLower(43);
            String phpSessId = StringUtils.getRandomStringLower(26);
            //第二步：获取token
            String token = getToken(code, cfdUid, phpSessId);
            //第三步：获取bd详情
            result = getDetails(code, token, cfdUid, phpSessId);
            ops.set(code, result, cacheTimeout, TimeUnit.HOURS);
        }
        return result;
    }

    private void resolveSkill(Map<String, Object> object) {
        resolvePersonSkills(object);
        resolveStarSkills(object);
    }

    private void resolveStarSkills(Map<String, Object> object) {
        List<Map<String, Object>> skills = (List<Map<String, Object>>) object.get("skills");
        List<Map<String, Object>> startSkills = skills.stream().filter(s -> s.get("skillName") == null).collect(Collectors.toList());
        for (Map<String, Object> skill : startSkills) {
            String name = (String) skill.get("name");
            String starName = getStarName(name);
            starName = starName
                    .replaceAll("\\^[a-zA-Z]", "")
                    .replaceAll("\\([\\s\\S]+\\)", "")
                    .replaceAll("\\{}", "")
                    .replaceAll("（[\\S\\s]+）", "")
                    .replaceAll("\\[[\\S\\s]+]", "")
            ;
            skill.put("starName", starName);
        }
        Map<String, Object> result = new HashMap<>();
        for (Map<String, Object> skill : startSkills) {
            if (result.containsKey(skill.get("starName"))) {
                Integer level = (Integer) result.get(skill.get("starName"));
                result.put((String) skill.get("starName"), level + 1);
            } else {
                result.put((String) skill.get("starName"), skill.get("level"));
            }
        }
        object.put("starSkills", result);
    }

    private void resolvePersonSkills(Map<String, Object> object) {
        List<Map<String, Object>> skills = (List<Map<String, Object>>) object.get("skills");
        List<Map<String, Object>> personSkills = new ArrayList<>();
        for (Map<String, Object> skill : skills) {
            if (skill == null) {
                continue;
            }
            String name = (String) skill.get("name");
            if (name != null) {
                String skillName = getSkillName(name);
                if (skillName != null) {
                    skillName = skillName
                            .replaceAll("\\^[a-zA-Z]", "")
                            .replaceAll("\\([\\s\\S]+\\)", "")
                            .replaceAll("<[\\S\\s]+>", "")
                    ;
                    skill.put("skillName", skillName);

                    String autoCastSkill = (String) skill.get("autoCastSkill");
                    if (autoCastSkill != null) {
                        String autoCastSkillName = getAutoCastSkillName(autoCastSkill);
                        if (autoCastSkillName != null) {
                            autoCastSkillName = autoCastSkillName.replaceAll("\\^[a-zA-Z]", "").replaceAll("\\([\\s\\S]+\\)", "");
                            skill.put("autoCastSkillName", autoCastSkillName);
                        } else {
                            skill.put("autoCastSkillName", "");
                        }
                    } else {
                        skill.put("autoCastSkill", "");
                        skill.put("autoCastSkillName", "");
                    }

                    personSkills.add(skill);
                }
            }
        }
        object.put("personSkills", personSkills);
    }

    private void resolveEquipment(Map<String, Object> object) {
        Map<String, Object> equipment = (Map<String, Object>) object.get("equipment");
        for (String key : equipment.keySet()) {
            resolveEquipment(equipment, key);
        }
    }

    private void resolveEquipment(Map<String, Object> equipment, String code) {
        Map<String, String> equipment1 = (Map<String, String>) equipment.get(code);

        String itemCode = equipment1.get("item");
        if (itemCode != null) {
            String item;
            if (code.equals("relic")) {
                item = getDName(itemCode);
            } else {
                item = getAName(itemCode);
            }
            if (item != null) {
                item = item.replaceAll("\\^[a-zA-Z]", "").replaceAll("\\([\\s\\S]+\\)", "");
                if (isLegendary(itemCode)) {
                    item = "神话的 " + item;
                }
                equipment1.put("itemName", item);
            }
        } else {
            equipment1.put("itemName", "");
        }

        String componentCode = equipment1.get("component");
        if (componentCode != null) {
            String component = getDName(componentCode);
            if (component != null) {
                component = component
                        .replaceAll("\\^[a-zA-Z]", "")
                        .replaceAll("\\([\\s\\S]+\\)", "")
                        .replaceAll("[a-zA-Z]\\d+", "");
                equipment1.put("componentName", component);
            }
        } else {
            equipment1.put("componentName", "");
        }

        String augmentCode = equipment1.get("augment");
        if (augmentCode != null) {
            String augment = getDName(augmentCode);
            if (augment != null) {
                augment = augment.replaceAll("\\^[a-zA-Z]", "").replaceAll("\\([\\s\\S]+\\)", "");
                equipment1.put("augmentName", augment);
            }
        } else {
            equipment1.put("augmentName", "");
        }

        String prefixCode = equipment1.get("prefix");
        if (prefixCode != null) {
            String prefix = getCName(prefixCode);
            if (prefix != null) {
                prefix = prefix.replaceAll("\\^[a-zA-Z]", "").replaceAll("\\([\\s\\S]+\\)", "");
                equipment1.put("prefixName", prefix);
            }
        } else {
            equipment1.put("prefixName", "");
        }

        String suffixCode = equipment1.get("suffix");
        if (suffixCode != null) {
            String suffix = getCName(suffixCode);
            if (suffix != null) {
                suffix = suffix.replaceAll("\\^[a-zA-Z]", "").replaceAll("\\([\\s\\S]+\\)", "");
                equipment1.put("suffixName", suffix);
            }
        } else {
            equipment1.put("suffixName", "");
        }
    }

    private Boolean isLegendary(String code) {
        String regex = code + ":\\{(?<result>[\\s\\S]+?)}";
        String result = RegexUtils.regex(itemJs, regex, "result");
        if (result == null) {
            return false;
        }
        return result.contains("tagStyleUniqueTier3");
    }

    private String isBuffSkill(String code) {
        String regex = code + ":\\{(?<result>[\\s\\S]+?)}";
        String result = RegexUtils.regex(calcJs, regex, "result");
        if (result == null) {
            return null;
        }
        if (result.contains("buffSkillName")) {
            return RegexUtils.regex(result, "buffSkillName:\"(?<result>[\\s\\S]+?)\"", "result");
        }
        return null;
    }

    private String getAName(String code) {
        String regex = code + ":\\{[\\s\\S]+?a:\"(?<result>[\\s\\S]+?)\"";
        String result = RegexUtils.regex(itemJs, regex, "result");
        return itemMap.get(result);
    }

    private String getCName(String code) {
        String regex = code + ":\\{[\\s\\S]+?c:\"(?<result>[\\s\\S]+?)\"";
        String result = RegexUtils.regex(itemJs, regex, "result");
        return itemMap.get(result);
    }

    private String getDName(String code) {
        String regex = code + ":\\{[\\s\\S]+?d:\"(?<result>[\\s\\S]+?)\"";
        String result = RegexUtils.regex(itemJs, regex, "result");
        return itemMap.get(result);
    }

    private String getSkillName(String code) {
        String regex = code + ":\\{name:\"(?<result>[\\s\\S]+?)\"";
        String result = RegexUtils.regex(itemJs, regex, "result");
        return itemMap.get(result);
    }

    private String getStarName(String code) {
        if ("sk1088".equals(code)) {
            System.out.println(1);
        }
        String buffSkill = isBuffSkill(code);
        if (buffSkill != null) {
            code = buffSkill;
        }
        String regex = code + ":\\{[\\s\\S]+?skillDisplayName:\"(?<result>[\\s\\S]+?)\"";
        String result = RegexUtils.regex(calcJs, regex, "result");
        return itemMap.get(result);
    }

    private String getAutoCastSkillName(String code) {
        String regex2 = code + ":\\{[\\s\\S]+?skillDisplayName:\"(?<result>[\\s\\S]+?)\"";
        String result2 = RegexUtils.regex(calcJs, regex2, "result");
        return itemMap.get(result2);
    }

    private String getToken(String code, String cfdUid, String phpSessId) throws IOException {
        Request request = new Request.Builder()
                .url("https://www.grimtools.com/calc/" + code)
                .method("GET", null)
                .addHeader("Cookie", "__cfduid=" + cfdUid + "; PHPSESSID=" + phpSessId)
                .build();
        Response response = client.newCall(request).execute();
        assert response.body() != null;
        return RegexUtils.regex(response.body().string(), "^[\\s\\S]+token=\"(?<token>[\\s\\S]+)\";[\\s\\S]+$", "token");
    }

    private String getDetails(String code, String token, String cfdUid, String phpSessId) throws IOException {
        OkHttpClient client = new OkHttpClient().newBuilder()
                .build();
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded; charset=UTF-8");
        RequestBody body = RequestBody.create(mediaType, "token=" + token);
        Request request = new Request.Builder()
                .url("https://www.grimtools.com/load_build.php?id=" + code)
                .method("POST", body)
                .addHeader("content-type", "application/x-www-form-urlencoded; charset=UTF-8")
                .addHeader("origin", "https://www.grimtools.com")
                .addHeader("x-requested-with", "XMLHttpRequest")
                .addHeader("Cookie", "__cfduid=" + cfdUid + "; PHPSESSID=" + phpSessId)
                .build();
        Response response = client.newCall(request).execute();
        assert response.body() != null;
        return response.body().string();
    }

}
